package com.tsfyun.scm.service.impl.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Stopwatch;
import com.tsfyun.common.base.config.OrikaBeanMapper;
import com.tsfyun.common.base.constant.LoginConstant;
import com.tsfyun.common.base.dto.ShortMessageDTO;
import com.tsfyun.common.base.dto.UploadFileDTO;
import com.tsfyun.common.base.enums.*;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.common.base.help.excel.ExcelUtil;
import com.tsfyun.common.base.security.LoginVO;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.util.*;
import com.tsfyun.common.base.validator.ValidatorUtils;
import com.tsfyun.common.base.vo.UploadFileVO;
import com.tsfyun.scm.dto.user.*;
import com.tsfyun.scm.dto.user.client.ClientChangePhoneCodeDTO;
import com.tsfyun.scm.dto.user.client.ClientChangePhoneDTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.system.SysDepartment;
import com.tsfyun.scm.entity.system.SysRole;
import com.tsfyun.scm.entity.user.Login;
import com.tsfyun.scm.entity.user.Person;
import com.tsfyun.scm.excel.PersonExcel;
import com.tsfyun.scm.mapper.user.LoginMapper;
import com.tsfyun.scm.mapper.user.PersonMapper;
import com.tsfyun.scm.security.config.StringRedisUtils;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.file.IUploadFileService;
import com.tsfyun.scm.service.system.ISysDepartmentService;
import com.tsfyun.scm.service.system.ISysRoleService;
import com.tsfyun.scm.service.third.IShortMessageService;
import com.tsfyun.scm.service.user.ICustomerPersonService;
import com.tsfyun.scm.service.user.ILoginService;
import com.tsfyun.scm.service.user.IPersonRoleService;
import com.tsfyun.scm.service.user.IPersonService;
import com.tsfyun.scm.util.AuthUserUtil;
import com.tsfyun.scm.util.TsfWeekendSqls;
import com.tsfyun.scm.vo.system.RolePersonVO;
import com.tsfyun.scm.vo.system.SysRoleVO;
import com.tsfyun.scm.vo.user.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


@Slf4j
@Service
@RefreshScope
public class PersonServiceImpl extends ServiceImpl<Person> implements IPersonService {

    @Autowired
    private PersonMapper personMapper;
    @Autowired
    private OrikaBeanMapper beanMapper;
    @Autowired
    private ILoginService loginService;
    @Autowired
    private ISysDepartmentService sysDepartmentService;
    @Autowired
    private ISysRoleService sysRoleService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ICustomerPersonService customerPersonService;
    @Autowired
    private IPersonRoleService personRoleService;
    @Autowired
    private IUploadFileService uploadFileService;
    @Value("${code.ilegal}")
    private String ilegalPersonCode;

    @Value("${scm.manage.person.code}")
    private String managePersonCode;

    @Autowired
    protected HttpServletRequest request;

    @Autowired
    private StringRedisUtils stringRedisUtils;

    @Autowired
    private Snowflake snowflake;

    @Value("${upload.excel.maxmum:500}")
    private int uploadMaxmum;

    @Autowired
    private LoginMapper loginMapper;

    @Autowired
    private IShortMessageService shortMessageService;

    @Override
    public Person findByCode(String code) {
        Person person = personMapper.findByCode(code);
        return person;
    }

    /**
     * 生成人员编号
     * @return
     */
    public String createPersonCode(){
        String code = "";
        for(int i=0;i<10;i++){
            code = RandomUtil.randomNumbers (6);
            Person condition = new Person();
            condition.setCode(code);
            if(super.count(condition)==0){
                break;
            }
        }
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(code),new ServiceException("系统繁忙请稍后重试"));
        return code;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clientRegister(RegisterDTO dto) {
        //1、新建客户信息
        Customer customer = customerService.saveEmptyInfo();
        log.info("保存客户成功：{}",customer.getId());
        //2、新建人员信息
        Person person = new Person();
        person.setCode(createPersonCode());
        person.setName(String.format("新用户%s",person.getCode()));
        person.setPosition(PositionEnum.OTHER.getCode());
        person.setGender(SexTypeEnum.UNKNOW.getCode());
        person.setIsClient(Boolean.TRUE);
        person.setCustomerId(customer.getId());
        person.setPhone(dto.getPhoneNo());
        personMapper.insertSelective(person);
        //3、建立人员和客户关系
        customerPersonService.bindCustomerPerson(customer.getId(),person.getId(),Boolean.TRUE);
        //4、授权人员角色权限
        personRoleService.saveOrUpdate(person.getId(),Arrays.asList("client_admin"));
        //5、写入登录表
        loginService.saveLogin(person.getId(),1,dto.getPhoneNo());
    }

    @Override
    public Person getById(Long id) {
        return super.getById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addInnerPerson(InsidePersonSaveDTO dto) {
        //当填写了密码后，需校验密码一致性，如果未填则采用默认密码
        checkSaveUser(dto);
        Person person = null;
        //校验手机号码和邮箱是否重复如果填写了的话（如果填写了手机号码的话，其实此处需要用验证码，因为只有验证码才能证明是真实有效的手机号码，暂时不加校验，且不会生成手机号码和邮箱的登录信息）
        //数据库已做了唯一校验
        try {
            person = beanMapper.map(dto,Person.class);
            person.setPassWord(StringUtils.encrySha(dto.getCode()));
            //管理端登录权限
            person.setIsManager(Boolean.TRUE);
            person.setIsClient(Boolean.FALSE);
            person.setIsSale(Boolean.FALSE);
            super.saveNonNull(person);
        } catch (DuplicateKeyException e) {
            if(e.getMessage().contains("uni_code")) {
                throw new ServiceException("员工编码已经存在");
            } else if (e.getMessage().contains("uni_phone")) {
                throw new ServiceException("手机号码已经存在");
            } else if (e.getMessage().contains("uni_mail")) {
                throw new ServiceException("邮箱已经存在");
            }
        }
        //生成登录信息
        Login login = new Login();
        login.setPersonId(person.getId());
        login.setLoginName(dto.getCode());
        login.setType(LoginTypeEnum.ACCOUNT.getCode());
        loginService.saveNonNull(login);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void editInnerPerson(InsidePersonSaveDTO dto) {
        checkSaveUser(dto);
        Person person = null;
        Person oldPerson = null;
        boolean needModifyCode = false;
        //校验手机号码和邮箱是否重复如果填写了的话（如果填写了手机号码的话，其实此处需要用验证码，因为只有验证码才能证明是真实有效的手机号码，暂时不加校验，且不会生成手机号码和邮箱的登录信息）
        //数据库已做了唯一校验
        try {
            oldPerson = super.getById(dto.getId());
            TsfPreconditions.checkArgument(Objects.nonNull(oldPerson),new ServiceException("员工信息不存在"));
            //admin管理员不允许修改编码
            if(Objects.equals(oldPerson.getCode(),managePersonCode) && !Objects.equals(oldPerson.getCode(),dto.getCode())) {
                throw new ServiceException("系统管理员不允许修改员工编号");
            }
            //客户端和销售端暂不允许修改
            TsfPreconditions.checkArgument(Objects.equals(oldPerson.getIsManager(),Boolean.TRUE),new ServiceException("非管理端用户不允许修改"));
            //自己不能把自己置为离职
            TsfPreconditions.checkArgument(!AuthUserUtil.checkSelfSetIncumbency(dto.getId(),dto.getIncumbency()),new ServiceException("您无法将自己置为离职状态"));
            //已删除的员工不能修改
            TsfPreconditions.checkArgument(!Objects.equals(oldPerson.getIsDelete(),Boolean.TRUE),new ServiceException("该员工已被移除，不允许修改"));

            person = beanMapper.map(dto,Person.class);
            person.setPassWord(StringUtils.encrySha(dto.getCode()));
            person.setDateCreated(oldPerson.getDateCreated());
            person.setCreateBy(oldPerson.getCreateBy());
            needModifyCode = !Objects.equals(oldPerson.getCode(),dto.getCode());
            person.setIsChangedPwd(!needModifyCode);//已经修改过密码
            person.setIsManager(oldPerson.getIsManager());
            person.setIsClient(oldPerson.getIsClient());
            person.setIsSale(oldPerson.getIsSale());
            person.setIsDelete(oldPerson.getIsDelete());
            super.updateById(person);
        } catch (DuplicateKeyException e) {
            if(e.getMessage().contains("uni_code")) {
                throw new ServiceException("员工编码已经存在");
            } else if (e.getMessage().contains("uni_phone")) {
                throw new ServiceException("手机号码已经存在");
            } else if (e.getMessage().contains("uni_mail")) {
                throw new ServiceException("邮箱已经存在");
            }
        }
        //如果修改了岗位，则需要修改redis中的数据；如果时把在职状态改为离职，则直接清空该人员的redis数据；
        if(!Objects.equals(dto.getIncumbency(),Boolean.TRUE)
            && !Objects.equals(dto.getIncumbency(),oldPerson.getIncumbency())) {
            //在职改为离职，强制退出
            loginService.forceLogout(person.getId().toString(),person.getName());
        }
        if(!Objects.equals(oldPerson.getPosition(),person.getPosition())) {
            //岗位变更
            String authPersonKey = LoginConstant.AUTH +  person.getId().toString();
            String authPerson = stringRedisUtils.getToString(authPersonKey);
            if(StringUtils.isNotEmpty(authPerson)) {
                LoginVO loginVO = JSONObject.parseObject(authPerson, LoginVO.class);
                loginVO.setPosition(person.getPosition());
                Long authPersonKeyTtl = stringRedisUtils.ttl(authPersonKey);
                if(-2 != authPersonKeyTtl) {
                    stringRedisUtils.set(authPersonKey,JSONObject.toJSONString(loginVO),authPersonKeyTtl, TimeUnit.SECONDS);
                }
            }
        }
        //修改登录信息
        //如果修改了编码则修改登录信息
        if(needModifyCode) {
            Login login = new Login();
            login.setPersonId(person.getId());
            login.setLoginName(dto.getCode());
            login.setType(LoginTypeEnum.ACCOUNT.getCode());
            loginService.updateLogin(login);
        }
    }

    public void checkSaveUser(InsidePersonSaveDTO dto) {
        //数据校验
        if(StringUtils.isNotEmpty(dto.getGender())) {
            TsfPreconditions.checkArgument(Objects.nonNull(SexTypeEnum.of(dto.getGender())),new ServiceException("性别格式不正确"));
        }
        if(StringUtils.isNotEmpty(dto.getPosition())) {
            TsfPreconditions.checkArgument(Objects.nonNull(PositionEnum.of(dto.getPosition())),new ServiceException("职位格式不正确"));
        }
        dto.setIncumbency(TypeUtils.castToBoolean(dto.getIncumbency(),Boolean.TRUE));
       //当填写了密码后，需校验密码一致性，如果未填则采用默认密码
        dto.setPhone(TypeUtils.castToString(dto.getPhone(),null));
        dto.setMail(TypeUtils.castToString(dto.getMail(),null));
        //校验员工编码是否非法
        List<String> ilegalCodes = Arrays.asList(ilegalPersonCode.split(","));
        if(null == dto.getId()){//修改则不验证
            TsfPreconditions.checkArgument(!ilegalCodes.contains(dto.getCode()),new ServiceException("员工编码非法，请填写其他编码"));
        }
        //为了区分普通的帐号编码和手机号码、邮箱的区分增加校验
        TsfPreconditions.checkArgument(AuthUserUtil.codePattern.matcher(dto.getCode()).find(),new ServiceException("员工编码只能包含字母+数字(不能纯数字)"));
        SysDepartment sysDepartment = sysDepartmentService.getById(dto.getDepartmentId());
        TsfPreconditions.checkArgument(Objects.nonNull(sysDepartment),new ServiceException("部门不存在"));
    }

    @Override
    public PersonInfoVO detailPerson(Long id) {
        Person person = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(person),new ServiceException("员工信息不存在"));
        PersonInfoVO personInfoVO = beanMapper.map(person, PersonInfoVO.class);
        Optional.ofNullable(person.getDepartmentId()).ifPresent(r->{
            SysDepartment department = sysDepartmentService.getById(person.getDepartmentId());
            personInfoVO.setDepartmentName(Objects.nonNull(department) ? department.getName() : null);
        });
        //获取用户的角色信息
        List<SysRole> roles = sysRoleService.getUserRoles(id);
        if(!CollectionUtils.isEmpty(roles)) {
            personInfoVO.setRoles(beanMapper.mapAsList(roles, SysRoleVO.class));
        }

        PositionEnum positionEnum = PositionEnum.of(person.getPosition());
        SexTypeEnum sexTypeEnum = SexTypeEnum.of(person.getGender());
        //职位
        personInfoVO.setPositionName(Objects.nonNull(positionEnum) ? positionEnum.getName() : null);
        //性别
        personInfoVO.setGenderName(Objects.nonNull(sexTypeEnum) ? sexTypeEnum.getName() : null);
        return personInfoVO;
    }

    @Override
    public PageInfo<PersonInfoVO> pageList(InsidePersonDTO dto,Boolean findRole) {
        //此处特殊处理，如果传了部门，则按部门递归分页查询
        List<PersonInfoVO> list;
        if(Objects.isNull(dto.getDepartmentId()) || Objects.equals(dto.getDepartmentId(),0)) {
            PageHelper.startPage(dto.getPage(),dto.getLimit());
            list = personMapper.list(dto);
        } else {
            //先递归获取当前部门及其以下的子部门
            List<Long> childDepartmentIds = sysDepartmentService.getSubDepartments(dto.getDepartmentId());
            PageHelper.startPage(dto.getPage(),dto.getLimit());
            //注意，分页必须是紧跟着的那个查询才会有效
            list = personMapper.listWithDepartmentIds(dto,childDepartmentIds);
        }
        if(CollectionUtil.isNotEmpty(list)) {
            list.stream().forEach(r->{
                PositionEnum positionEnum = PositionEnum.of(r.getPosition());
                SexTypeEnum sexTypeEnum = SexTypeEnum.of(r.getGender());
                r.setPositionName(Objects.nonNull(positionEnum) ? positionEnum.getName() : null);
                r.setGenderName(Objects.nonNull(sexTypeEnum) ? sexTypeEnum.getName() : null);
                r.setIncumbencyName(Objects.nonNull(r.getIncumbency()) && r.getIncumbency() ? IncumbencyEnum.INCUMBENCY.getName() : IncumbencyEnum.LEAVE.getName());
            });
        }
        //查询角色信息并返回
        if (findRole && CollectionUtil.isNotEmpty(list)) {
            try {
                    List<Long> personIds = list.stream().map(PersonInfoVO::getId).collect(Collectors.toList());
                    //查询出这些员工id的角色信息，然后按员工分组
                    List<RolePersonVO> roles = sysRoleService.getUserRolesBatch(personIds);
                    if (CollectionUtil.isNotEmpty(roles)) {
                        Map<Long, List<RolePersonVO>> rolePersonMap = roles.stream().collect(Collectors.groupingBy(RolePersonVO::getPersonId));
                        list.stream().forEach(r -> {
                            List<RolePersonVO> rolePersonVOS = rolePersonMap.get(r.getId());
                            if(CollectionUtil.isNotEmpty(rolePersonVOS)) {
                                List<String> roleNames = rolePersonVOS.stream().map(RolePersonVO::getName).map(StringUtils::null2EmptyWithTrim).collect(Collectors.toList());
                                String roleNameStr = CollUtil.join(roleNames, ",");
                                r.setRoleNames(roleNameStr);
                            }
                        });
                    }
            } catch (Exception e) {
                log.error("获取角色信息异常",e);
            }
        }
        return new PageInfo(list);
    }

    @Override
    public LoginVO info() {
        LoginVO loginVO = SecurityUtil.getCurrent();
        return loginVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Long id) {
        Person person = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(person),new ServiceException("员工信息不存在"));
        //客户端和销售端暂不允许修改
        TsfPreconditions.checkArgument(Objects.equals(person.getIsManager(),Boolean.TRUE),new ServiceException("非管理端用户不允许修改"));
        //超级管理员不允许删除
        TsfPreconditions.checkArgument(!Objects.equals(managePersonCode,person.getCode()),new ServiceException("系统管理员不允许删除"));
        //当前登录用户不允许删除
        TsfPreconditions.checkArgument(!Objects.equals(id, SecurityUtil.getCurrent().getPersonId()),new ServiceException("当前用户不能删除"));
        TsfPreconditions.checkArgument(!Objects.equals(person.getIsDelete(),Boolean.TRUE),new ServiceException("该员工已经被删除了"));
        //调整成逻辑删除（保留登录信息以免后续需要恢复）
        personMapper.deletePerson(id);
        //如果该人员正在登录系统，需要剔除登录
        loginService.forceLogout(id.toString(),person.getName());
    }

    @Override
    public int countByDepartmentId(Long departmentId) {
        Person condition = new Person();
        condition.setDepartmentId(departmentId);
        return super.count(condition);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePwdFlag(Long id, Boolean flag) {
        Person update = new Person();
        update.setIsChangedPwd(flag);
        personMapper.updateByExampleSelective(update, Example.builder(Person.class).where(WeekendSqls.<Person>custom()
                .andEqualTo(Person::getId, id)).build());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateIncumbency(Long id, Boolean incumbency) {
        Person person = super.getById(id);
        TsfPreconditions.checkArgument(Objects.nonNull(person),new ServiceException("员工信息不存在"));
        TsfPreconditions.checkArgument(!Objects.equals(person.getCode(),managePersonCode),new ServiceException("此账号为管理员不允许此操作"));
        //自己不能把自己置为离职
        TsfPreconditions.checkArgument(!AuthUserUtil.checkSelfSetIncumbency(id,incumbency),new ServiceException("您无法将自己置为离职状态"));
        //客户端和销售端暂不允许修改
        TsfPreconditions.checkArgument(Objects.equals(person.getIsManager(),Boolean.TRUE),new ServiceException("非管理端用户不允许修改"));
        Person update = new Person();
        update.setIncumbency(incumbency);
        personMapper.updateByExampleSelective(update, Example.builder(Person.class).where(WeekendSqls.<Person>custom()
                .andEqualTo(Person::getId, id)).build());
        //如果该登录员工存在token登录信息则剔除
        if(!incumbency) {
            loginService.forceLogout(id.toString(),person.getName());
        }
    }

    @Override
    public PageInfo<Person> positionList(InsidePersonDTO dto) {
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(dto.getPosition()),new ServiceException("职位类型不能为空"));
        TsfPreconditions.checkArgument(Optional.ofNullable(PositionEnum.of(dto.getPosition())).isPresent(),new ServiceException("职位类型错误"));
        Person condition = new Person();
        condition.setPosition(dto.getPosition());
        condition.setIsDelete(Boolean.FALSE);
        PageInfo<Person> pageInfo = super.pageList(condition,dto.getPage(),dto.getLimit(),super.getOrderBy("name",SortTypeEnum.ASC.getCode()));
        return pageInfo;
    }

    @Override
    public Map<Long,SimplePersonInfo> findByIds(Set<Long> ids) {
        if(CollectionUtil.isEmpty(ids)) {
            return null;
        }
        //过滤掉null的id
        ids = ids.stream().filter(r->Objects.nonNull(r)).collect(Collectors.toSet());
        if(!CollectionUtils.isEmpty(ids)) {
            List<Person> peoples = personMapper.selectByExample(Example.builder(Person.class).where(TsfWeekendSqls.<Person>custom()
                    .andIn(false, Person::getId, ids)).select("id", "code", "name").build());
            if (CollectionUtil.isNotEmpty(peoples)) {
                List<SimplePersonInfo> simplePersonInfos = beanMapper.mapAsList(peoples, SimplePersonInfo.class);
                if(CollectionUtil.isNotEmpty(simplePersonInfos)) {
                    return simplePersonInfos.stream().collect(Collectors.toMap(item->item.getId(),item->item));
                }
            }
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importExcel(MultipartFile file) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        List<PersonExcel> dataList = null;
        try {
            dataList = ExcelUtil.readExcel(file, PersonExcel.class, 1);
        } catch (Exception e) {
            log.error("读取导入的excel文件异常",e);
            throw new ServiceException("导入数据异常，请仔细检查您的导入文件是否符合模板要求");
        }
        if(CollectionUtils.isEmpty(dataList)) {
            throw new ServiceException("本次没有可以导入的数据");
        }
        TsfPreconditions.checkArgument(dataList.size() <= uploadMaxmum,new ServiceException(String.format("您本次导入的数据超过%d条的上限了",uploadMaxmum)));
        //校验数据正确性
        List<PersonExcel> finalDataList = dataList;
        IntStream.range(0,dataList.size()).forEach(idx->{
            PersonExcel personExcel = finalDataList.get(idx);
            int contentRowNo = idx + 1;
            ValidatorUtils.validateEntity(personExcel,contentRowNo);
            //校验职位信息是否正确，不赋默认值
            PositionEnum positionEnum = PositionEnum.ofName(personExcel.getPositionName());
            Optional.ofNullable(positionEnum).orElseThrow(()->new ServiceException(String.format("第%d行职位信息填写不正确",contentRowNo)));
            //如果未填写则默认为未知
            SexTypeEnum sexTypeEnum = SexTypeEnum.UNKNOW;
            if(StringUtils.isNotEmpty(personExcel.getGenderName())) {
                sexTypeEnum = SexTypeEnum.ofName(personExcel.getGenderName());
                TsfPreconditions.checkArgument(Objects.nonNull(sexTypeEnum),new ServiceException(String.format("第%d行性别信息填写不正确",contentRowNo)));
            }
            //校验员工编码是否非法
            List<String> ilegalCodes = Arrays.asList(ilegalPersonCode.split(","));
            TsfPreconditions.checkArgument(!ilegalCodes.contains(personExcel.getCode()),new ServiceException(String.format("第%d行员工编码非法",contentRowNo)));
            //为了区分普通的帐号编码和手机号码、邮箱的区分增加校验
            TsfPreconditions.checkArgument(AuthUserUtil.codePattern.matcher(personExcel.getCode()).find(),new ServiceException(String.format("第%d行员工编码格式错误，只能包含字母+数字(不能纯数字)",contentRowNo)));
            //校验部门信息正确性
            SysDepartment sysDepartment = sysDepartmentService.findOneByName(personExcel.getDepartmentName());
            Optional.ofNullable(sysDepartment).orElseThrow(()->new ServiceException(String.format("第%d行部门填写不正确",contentRowNo)));

            Person person = beanMapper.map(personExcel,Person.class);
            person.setPosition(positionEnum.getCode());
            person.setGender(sexTypeEnum.getCode());
            person.setIncumbency(Boolean.TRUE);
            person.setDepartmentId(sysDepartment.getId());
            person.setPassWord(StringUtils.encrySha(person.getCode()));
            person.setIsChangedPwd(Boolean.FALSE);
            person.setIsDelete(Boolean.FALSE);
            person.setIsManager(Boolean.TRUE);
            person.setIsClient(Boolean.FALSE);
            person.setIsSale(Boolean.FALSE);
            try {
                super.saveNonNull(person);
            } catch (DuplicateKeyException e) {
                if(e.getMessage().contains("uni_code")) {
                    throw new ServiceException(String.format("第%d行员工编码已经存在",contentRowNo));
                } else if (e.getMessage().contains("uni_phone")) {
                    throw new ServiceException(String.format("第%d行手机号码已经存在",contentRowNo));
                } else if (e.getMessage().contains("uni_mail")) {
                    throw new ServiceException(String.format("第%d行邮箱已经存在",contentRowNo));
                }
            }
            //生成登录信息
            Login login = new Login();
            login.setPersonId(person.getId());
            login.setLoginName(person.getCode());
            login.setType(LoginTypeEnum.ACCOUNT.getCode());
            loginService.saveNonNull(login);
        });
        stopwatch.stop();
        log.info("本次成功导入{}条数据，耗时：{}秒",dataList.size(),stopwatch.elapsed(TimeUnit.SECONDS));
    }

    @Override
    public List<SimplePersonInfo> getPersonByMenuId(@NonNull String menuId) {
        return personMapper.getPersonByMenuId(menuId);
    }

    @Override
    public List<SimplePersonInfo> getPersonByRoleId(@NonNull String roleId) {
        return personMapper.getPersonByRoleId(roleId);
    }

    @Override
    public void updatePersonPwdChangeFlag(String password,Long id) {
        Person update = new Person();
        update.setIsChangedPwd(Boolean.FALSE);
        update.setPassWord(password);
        personMapper.updateByExampleSelective(update,Example.builder(Person.class).where(TsfWeekendSqls.<Person>custom().andEqualTo(false,Person::getId,id)).build());
    }

    @Override
    public Integer countPersonNumber(){
        return personMapper.countPersonNumber();
    }

    @Override
    public Integer checkPersonIsDelete(Long id) {
        Person person = super.getById(id);
        Optional.ofNullable(person).orElseThrow(()->new ServiceException("员工信息不存在"));
        return Objects.equals(person.getIsDelete(),Boolean.TRUE) ? 1 : 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadHead(MultipartFile file) {
        LoginVO loginVO = SecurityUtil.getCurrent();
        UploadFileDTO dto = FileUtil.wrapUploadFileDto(file,FileTypeEnum.IMAGE.getCode(),loginVO.getPersonId().toString(),"person","person_head","用户头像");
        dto.setIsOnlyOne(Boolean.TRUE);
        UploadFileVO vo = uploadFileService.upload(dto);
        String head = String.format("/scm/download/file/%s",vo.getId().toString());
        Person person = super.getById(loginVO.getPersonId());
        person.setHead(head);
        super.updateById(person);
        //更新用户缓存数据
        loginVO.setUserHead(head);
        stringRedisUtils.set(LoginConstant.AUTH.concat(person.getId().toString()),JSONObject.toJSONString(loginVO));
        return head;
    }

    @Override
    public ClientPersonInfoVO loginPersonInfo() {
        Person person = getById(SecurityUtil.getCurrentPersonId());
        ClientPersonInfoVO clientPersonInfoVO = beanMapper.map(person,ClientPersonInfoVO.class);
        clientPersonInfoVO.setSetPassword(StringUtils.isNotEmpty(person.getPassWord()));
        //判断用户是否绑定微信登录
        clientPersonInfoVO.setBindWx(loginService.checkUserBindWxLogin(person.getId()));
        return clientPersonInfoVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLoginPersonInfo(ClientPersonInfoDTO dto) {
        LoginVO loginVO = SecurityUtil.getCurrent();
        Person person = getById(loginVO.getPersonId());
        person.setName(dto.getName());
        person.setGender(dto.getGender());
        person.setMail(dto.getMail());
        super.updateById(person);
        //更新用户缓存数据
        loginVO.setPersonName(person.getName());
        stringRedisUtils.set(LoginConstant.AUTH.concat(person.getId().toString()),JSONObject.toJSONString(loginVO));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePhoneSendCode() {
        Long personId = SecurityUtil.getCurrentPersonId();
        //获取人员信息
        Person person = super.getById(personId);
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(person.getPhone()),new ServiceException("您还未绑定手机号码"));
        //发送短信认证
        ShortMessageDTO shortMessageDTO = new ShortMessageDTO();
        shortMessageDTO.setPhoneNo(person.getPhone());
        shortMessageService.sendVerificationCode(shortMessageDTO,MessageNodeEnum.UNBIND_PHONE, RequestUtils.getIp());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePhoneValidate(String validateCode) {
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(validateCode),new ServiceException("请输入短信验证码"));
        TsfPreconditions.checkArgument(StringUtils.null2EmptyWithTrim(validateCode).length() == 4,new ServiceException("短信验证码输入错误"));
        Long personId = SecurityUtil.getCurrentPersonId();
        //获取人员信息
        Person person = super.getById(personId);
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(person.getPhone()),new ServiceException("您还未绑定手机号码"));
        //验证短信验证码
        shortMessageService.checkVerificationCode(person.getPhone(),MessageNodeEnum.UNBIND_PHONE,validateCode);
        stringRedisUtils.set(LoginConstant.UNBIND_PHONE.concat(person.getPhone()),validateCode,8L,TimeUnit.MINUTES);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePhoneBindSendCode(ClientChangePhoneCodeDTO dto) {
        Long personId = SecurityUtil.getCurrentPersonId();
        //获取人员信息
        Person person = super.getById(personId);
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        TsfPreconditions.checkArgument(!Objects.equals(dto.getPhoneNo(),person.getPhone()),new ServiceException("新手机号码不能与原手机号码一样"));
        Login login = loginService.findByLoginName(dto.getPhoneNo());
        TsfPreconditions.checkArgument(Objects.isNull(login),new ServiceException("该手机号码已绑定其他帐号，请更换其他手机号码"));
        Boolean isVerifyed = stringRedisUtils.exists(LoginConstant.UNBIND_PHONE.concat(person.getPhone()));
        TsfPreconditions.checkArgument(isVerifyed,new ServiceException("您的验证信息已失效，请重新操作"));
        log.info("用户id：【{}】，用户名称：【{}】，正在修改手机号码，原手机号码：【{}】，新手机号码【{}】",person.getPhone(),dto.getPhoneNo());
        //发送短信认证
        ShortMessageDTO shortMessageDTO =  new ShortMessageDTO();
        shortMessageDTO.setPhoneNo(dto.getPhoneNo());
        shortMessageService.sendVerificationCode(shortMessageDTO,MessageNodeEnum.BIND_PHONE, RequestUtils.getIp());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePhone(ClientChangePhoneDTO dto) {
        Long personId = SecurityUtil.getCurrentPersonId();
        //获取人员信息
        Person person = super.getById(personId);
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        TsfPreconditions.checkArgument(!Objects.equals(dto.getPhoneNo(),person.getPhone()),new ServiceException("新手机号码不能与原手机号码一样"));
        //验证新手机号码是否已被占用
        Login login = loginService.findByLoginName(dto.getPhoneNo());
        TsfPreconditions.checkArgument(Objects.isNull(login),new ServiceException("该手机号码已绑定其他帐号，请更换其他手机号码"));
        //验证短信验证码
        shortMessageService.checkVerificationCode(dto.getPhoneNo(),MessageNodeEnum.BIND_PHONE,dto.getValidateCode());
        Person updatePerson = new Person();
        updatePerson.setPhone(dto.getPhoneNo());
        personMapper.updateByExampleSelective(updatePerson,Example.builder(Person.class).where(
                TsfWeekendSqls.<Person>custom().andEqualTo(false,Person::getId, person.getId())).build());
        //修改人员登录的手机号码
        loginService.updateLoginPhone(personId,person.getPhone(),dto.getPhoneNo());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeMail(String mail) {
        LoginVO loginVO = SecurityUtil.getCurrent();
        //获取人员信息
        Person person = super.getById(loginVO.getPersonId());
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        Person update = new Person();
        update.setMail(mail);
        try {
            personMapper.updateByExampleSelective(update, Example.builder(Person.class).where(
                    TsfWeekendSqls.<Person>custom().andEqualTo(false, Person::getId, loginVO.getPersonId())).build());
        } catch (DuplicateKeyException e) {
            throw new ServiceException("邮箱已被占用,请修改");
        }
        //更新用户缓存数据
        loginVO.setEmail(mail);
        String loginJson;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            loginJson = objectMapper.writeValueAsString(loginVO);
        } catch (JsonProcessingException e) {
            throw new ServiceException("系统繁忙，请稍后再试");
        }
        stringRedisUtils.set(LoginConstant.AUTH.concat(person.getId().toString()),loginJson);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeGender(String gender) {
        LoginVO loginVO = SecurityUtil.getCurrent();
        //获取人员信息
        Person person = super.getById(loginVO.getPersonId());
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        Person update = new Person();
        update.setGender(gender);
        personMapper.updateByExampleSelective(update, Example.builder(Person.class).where(
                TsfWeekendSqls.<Person>custom().andEqualTo(false,Person::getId, loginVO.getPersonId())).build());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeNick(String nick) {
        LoginVO loginVO = SecurityUtil.getCurrent();
        //获取人员信息
        Person person = super.getById(loginVO.getPersonId());
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        Person update = new Person();
        update.setName(nick);
        personMapper.updateByExampleSelective(update, Example.builder(Person.class).where(
                TsfWeekendSqls.<Person>custom().andEqualTo(false,Person::getId, loginVO.getPersonId())).build());

        //更新用户缓存数据
        loginVO.setPersonName(update.getName());
        String loginJson;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            loginJson = objectMapper.writeValueAsString(loginVO);
        } catch (JsonProcessingException e) {
            throw new ServiceException("系统繁忙，请稍后再试");
        }
        stringRedisUtils.set(LoginConstant.AUTH.concat(person.getId().toString()),loginJson);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePassWord(UpdatePassWordDTO dto) {
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(dto.getPassWord()),new ServiceException("原密码不能为空"));
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(dto.getNewPassWord()),new ServiceException("新密码不能为空"));
        TsfPreconditions.checkArgument(!Objects.equals(dto.getPassWord(),dto.getNewPassWord()),new ServiceException("新密码不能与原密码一样"));
        //加密密码
        dto.setPassWord(StringUtils.encrySha(dto.getPassWord()));
        dto.setNewPassWord(StringUtils.encrySha(dto.getNewPassWord()));
        LoginVO loginVO = SecurityUtil.getCurrent();
        int cnt = personMapper.modifyPwd(loginVO.getPersonId(),dto.getPassWord(),dto.getNewPassWord(),String.format("%s/%s",loginVO.getPersonId(),loginVO.getPersonName()));
        TsfPreconditions.checkArgument(cnt > 0,new ServiceException("原密码不正确"));
        //修改密码退出登录
        loginService.logout();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setPassword(SetPassWordDTO dto) {
        TsfPreconditions.checkArgument(Objects.equals(dto.getPassWord(),dto.getComfirPassWord()),new ServiceException("两次输入的密码不一致"));
        Object personIdObj = stringRedisUtils.get(LoginConstant.PWD_CHANGE + dto.getAccessId());
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(personIdObj),new ServiceException("操作已失效，请重试"));
        //获取员工信息
        Person person = super.getById(Long.valueOf(StringUtils.null2EmptyWithTrim(personIdObj)));
        if(Objects.isNull(person)) {
            log.error("redis中存在该accessId的信息，但员工信息却有问题，accessId：{}",dto.getAccessId());
            throw new ServiceException("操作已失效，请重试");
        }
        personMapper.updatePwd(person.getId(),StringUtils.encrySha(dto.getPassWord()),String.format("%s/%s",person.getId(),person.getName()));
        //修改员工表的是否修改密码标志位
        updatePwdFlag(person.getId(),Boolean.TRUE);
        //清除accessId
        stringRedisUtils.remove(LoginConstant.PWD_CHANGE + dto.getAccessId());
    }

    @Override
    public Person getPhoneByCustomerId(Long customerId) {
        if(Objects.isNull(customerId)) {
            return null;
        }
        List<Person> persons = personMapper.selectByExample(Example.builder(Person.class).where(TsfWeekendSqls.<Person>custom()
                .andEqualTo(false, Person::getCustomerId, customerId)).select("phone","name","mail","wxgzhOpenid","wxxcxOpenid").build());
        return CollUtil.isNotEmpty(persons) ? persons.get(0) : null;
    }

    @Override
    public Map<Long, Person> getPhoneByCustomerIdBatch(List<Long> customerIds) {
        if(CollUtil.isEmpty(customerIds)) {
            return null;
        }
        List<Person> persons = personMapper.selectByExample(Example.builder(Person.class).where(TsfWeekendSqls.<Person>custom()
                .andIn(false, Person::getCustomerId, customerIds)).select("id","customerId","phone").build());
        Map<Long,Person> personMap = persons.stream().collect(Collectors.toMap(Person::getCustomerId, Function.identity()));
        return personMap;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unsubscribeWxgzh(String wxgzhId) {
        personMapper.setNullWxgzh(wxgzhId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePersonByWeixin(ThreeLoginVO vo) {
        Person update = new Person();
        update.setName(vo.getNickName());
        update.setHead(vo.getAvatarUrl());
        update.setGender(vo.getGender());
        personMapper.updateByExampleSelective(update,Example.builder(Person.class).where(
                TsfWeekendSqls.<Person>custom().andEqualTo(false,Person::getWxxcxOpenid, vo.getOpenId())).build());
        //更新用户缓存数据
        LoginVO loginVO = SecurityUtil.getCurrent();
        loginVO.setUserHead(vo.getAvatarUrl());
        loginVO.setPersonName(vo.getNickName());
        stringRedisUtils.set(LoginConstant.AUTH.concat(SecurityUtil.getCurrentPersonId().toString()),JSONObject.toJSONString(loginVO));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void subscribeWxgzh(Long personId,String wxgzhId) {
        Person update = new Person();
        update.setWxgzhOpenid(wxgzhId);
        personMapper.updateByExampleSelective(update,Example.builder(Person.class).where(
                TsfWeekendSqls.<Person>custom().andEqualTo(false,Person::getId, personId)).build());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePersonCustomerId(Long personId, Long customerId) {
        Person person = getById(personId);
        person.setCustomerId(customerId);
        super.updateById(person);
    }
}
